package top.codedance.iotp.manage.client;

import top.codedance.iotp.common.entity.CommonEntity;
import top.codedance.iotp.common.handler.AbsractIdleHandler;
import top.codedance.iotp.common.protocol.CommonDecoderCodec;
import top.codedance.iotp.manage.Console;
import top.codedance.iotp.manage.client.handler.IdleChannelHandler;
import top.codedance.iotp.manage.client.handler.ServerReponseHandler;
import top.codedance.iotp.manage.client.handler.RSAEncryptChannelHandler;
import top.codedance.iotp.manage.client.listenner.ConnectionListener;
import cd.io.netty.bootstrap.Bootstrap;
import cd.io.netty.channel.*;
import cd.io.netty.channel.nio.NioEventLoopGroup;
import cd.io.netty.channel.socket.SocketChannel;
import cd.io.netty.channel.socket.nio.NioSocketChannel;
import cd.io.netty.handler.logging.LogLevel;
import cd.io.netty.handler.logging.LoggingHandler;
import cd.io.netty.handler.timeout.IdleStateHandler;

import java.util.Arrays;

public class Client {
    private static boolean isUserClosed = false;
    private ChannelFuture channelFuture;
    private String host;
    private int port;

    public Client(String host, int port){
        this.host = host;
        this.port = port;
    }

    private EventLoopGroup group = new NioEventLoopGroup();

    public void run() {
        createBootstrap(new Bootstrap());
    }

    public void createBootstrap(Bootstrap bootstrap){
        try {
            Bootstrap group = bootstrap.group(this.group);
            if(Arrays.asList(LogLevel.values(), String.class).contains(Console.LOGGER_LEVEL)) {
                group.handler(new LoggingHandler(LogLevel.valueOf(Console.LOGGER_LEVEL)));
            }
            group.channel(NioSocketChannel.class)
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast("commonDecoderCodec", new CommonDecoderCodec(Console.getLogger(Console.class)))
                            .addLast("idleStateHandler", new IdleStateHandler(0, 0, 5))
                            .addLast(new IdleChannelHandler(Client.this, Console.getLogger(AbsractIdleHandler.class)))
                            .addLast(new RSAEncryptChannelHandler())
                            .addLast(new ServerReponseHandler());
                        }
                    });
            channelFuture = bootstrap.connect(host, port).sync();
            if (channelFuture.isSuccess()) {
                Console.syslogInfo(String.format("Connect server(%s:%s) success", host, port));
            }
            channelFuture.addListener(new ConnectionListener(Client.this));
            channelFuture.channel().closeFuture().sync();
            Console.syslogInfo("console close success");
        } catch (InterruptedException e) {
            System.out.println("Connect server fail.");
        } finally {
            group.shutdownGracefully();
        }
    }

    public void send(final CommonEntity commonEntity){
        Console.workerPool.submit(() -> {
            channelFuture.channel().writeAndFlush(commonEntity);
        });
    }

    public void shutdown(){
        isUserClosed = true;
        group.shutdownGracefully();
    }

    public static boolean isUserClosed() {
        return isUserClosed;
    }
}
